import java.util.Random;

public class Main {


  public static final int MIN_LENGTH = 1;
  public static final int MAX_LENGTH = 25;


  public static void main (String[] args) {
    try {
      long n = -1;
      long i = -1;
      long d = -1;
      for (;;) {
        integerRound();
        decimalRound();
        trigger();
        long nn = CInteger.buffers();
        long ni = CInteger.count();
        long nd = CDecimal.count();
        if (n != nn || i != ni || d != nd)
          print("[ %5d | %5d | %5d", n = nn, i = ni, d = nd);
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  private static RoundMode[] modes = RoundMode.values();

  public static void trigger () throws Exception {
    //Thread.sleep(1);
  }

  public static void abort (String fmt, Object... args) throws Exception {
    throw new Exception(String.format(fmt, args));
  }

  public static void print (String fmt, Object... args) {
    System.err.println(String.format(fmt, args));
  }


  public static void integerRound () throws Exception {
    Integer.Meta jmeta, cmeta;

    String str1 = makeInteger(MIN_LENGTH, MAX_LENGTH);
    Integer jop1 = new JInteger(str1);
    Integer cop1 = new CInteger(str1);
    Integer.Meta jmeta1 = jop1.meta();
    Integer.Meta cmeta1 = cop1.meta();
    if (!jmeta1.equals(cmeta1))
      abort("%s:\n%s\n%s", str1, jmeta1, cmeta1);

    int exp1 = (int)(Math.random() * 10);
    Integer jpow1 = jop1.pow(exp1);
    Integer cpow1 = cop1.pow(exp1);
    jmeta = jpow1.meta();
    cmeta = cpow1.meta();
    jpow1.release();
    cpow1.release();
    if (!jmeta.equals(cmeta))
      abort("%s^%d:\n%s\n%s", jmeta1.value, exp1, jmeta, cmeta);

    String str2 = makeInteger(MIN_LENGTH, MAX_LENGTH);
    Integer jop2 = new JInteger(str2);
    Integer cop2 = new CInteger(str2);
    Integer.Meta jmeta2 = jop2.meta();
    Integer.Meta cmeta2 = cop2.meta();
    if (!jmeta2.equals(cmeta2))
      abort("%s:\n%s\n%s", str2, jmeta2, cmeta2);

    int exp2 = (int)(Math.random() * 10);
    Integer jpow2 = jop2.pow(exp2);
    Integer cpow2 = cop2.pow(exp2);
    jmeta = jpow2.meta();
    cmeta = cpow2.meta();
    jpow2.release();
    cpow2.release();
    if (!jmeta.equals(cmeta))
      abort("%s^%d:\n%s\n%s", jmeta2.value, exp2, jmeta, cmeta);


    Integer jadd = jop1.add(jop2);
    Integer cadd = cop1.add(cop2);
    jmeta = jadd.meta();
    cmeta = cadd.meta();
    jadd.release();
    cadd.release();
    if (!jmeta.equals(cmeta))
      abort("%s + %s:\n%s\n%s", jmeta1.value, jmeta2.value, jadd.meta(), cadd.meta());

    Integer jsub = jop1.sub(jop2);
    Integer csub = cop1.sub(cop2);
    jmeta = jsub.meta();
    cmeta = csub.meta();
    jsub.release();
    csub.release();
    if (!jmeta.equals(cmeta))
      abort("%s - %s:\n%s\n%s", jmeta1.value, jmeta2.value, jsub.meta(), csub.meta());

    Integer jmul = jop1.mul(jop2);
    Integer cmul = cop1.mul(cop2);
    jmeta = jmul.meta();
    cmeta = cmul.meta();
    jmul.release();
    cmul.release();
    if (!jmeta.equals(cmeta))
      abort("%s * %s:\n%s\n%s", jmeta1.value, jmeta2.value, jmul.meta(), cmul.meta());

    if (jmeta2.sign != 0) {
      Integer jdiv = jop1.div(jop2);
      Integer cdiv = cop1.div(cop2);
      jmeta = jdiv.meta();
      cmeta = cdiv.meta();
      jdiv.release();
      cdiv.release();
      if (!jmeta.equals(cmeta))
        abort("%s / %s:\n%s\n%s", jmeta1.value, jmeta2.value, jdiv.meta(), cdiv.meta());

      if (jmeta2.sign > 0) {
        Integer jmod = jop1.mod(jop2);
        Integer cmod = cop1.mod(cop2);
        jmeta = jmod.meta();
        cmeta = cmod.meta();
        jmod.release();
        cmod.release();
        if (!jmeta.equals(cmeta))
          abort("%s % %s:\n%s\n%s", jmeta1.value, jmeta2.value, jmod.meta(), cmod.meta());
      }
    }

    jop1.release();
    cop1.release();
    jop2.release();
    cop2.release();
  }


  public static void decimalRound () throws Exception {
    Decimal.Meta jmeta, cmeta;

    String str1 = makeDecimal(MIN_LENGTH, MAX_LENGTH, MIN_LENGTH, MAX_LENGTH);
    Decimal jop1 = new JDecimal(str1);
    Decimal cop1 = new CDecimal(str1);
    Decimal.Meta jmeta1 = jop1.meta();
    Decimal.Meta cmeta1 = cop1.meta();
    if (!jmeta1.equals(cmeta1))
      abort("%s:\n%s\n%s", str1, jmeta1, cmeta1);

    int exp1 = (int)(Math.random() * 10);
    Decimal jpow1 = jop1.pow(exp1);
    Decimal cpow1 = cop1.pow(exp1);
    jmeta = jpow1.meta();
    cmeta = cpow1.meta();
    jpow1.release();
    cpow1.release();
    if (!jmeta.equals(cmeta))
      abort("%s^%d:\n%s\n%s", jmeta1.value, exp1, jmeta, cmeta);

    String str2 = makeDecimal(MIN_LENGTH, MAX_LENGTH, MIN_LENGTH, MAX_LENGTH);
    Decimal jop2 = new JDecimal(str2);
    Decimal cop2 = new CDecimal(str2);
    Decimal.Meta jmeta2 = jop2.meta();
    Decimal.Meta cmeta2 = cop2.meta();
    if (!jmeta2.equals(cmeta2))
      abort("%s:\n%s\n%s", str2, jmeta2, cmeta2);

    int exp2 = (int)(Math.random() * 10);
    Decimal jpow2 = jop2.pow(exp2);
    Decimal cpow2 = cop2.pow(exp2);
    jmeta = jpow2.meta();
    cmeta = cpow2.meta();
    jpow2.release();
    cpow2.release();
    if (!jmeta.equals(cmeta))
      abort("%s^%d:\n%s\n%s", jmeta2.value, exp2, jmeta, cmeta);


    Decimal jadd = jop1.add(jop2);
    Decimal cadd = cop1.add(cop2);
    jmeta = jadd.meta();
    cmeta = cadd.meta();
    jadd.release();
    cadd.release();
    if (!jmeta.equals(cmeta))
      abort("%s + %s:\n%s\n%s", jmeta1.value, jmeta2.value, jmeta, cmeta);

    Decimal jsub = jop1.sub(jop2);
    Decimal csub = cop1.sub(cop2);
    jmeta = jsub.meta();
    cmeta = csub.meta();
    jsub.release();
    csub.release();
    if (!jmeta.equals(cmeta))
      abort("%s - %s:\n%s\n%s", jmeta1.value, jmeta2.value, jmeta, cmeta);

    Decimal jmul = jop1.mul(jop2);
    Decimal cmul = cop1.mul(cop2);
    jmeta = jmul.meta();
    cmeta = cmul.meta();
    jmul.release();
    cmul.release();
    if (!jmeta.equals(cmeta))
      abort("%s * %s:\n%s\n%s", jmeta1.value, jmeta2.value, jmeta, cmeta);

    if (jmeta2.sign != 0) {
      RoundMode mode = modes[(int)(Math.random() * modes.length)];
      Decimal jdiv = jop1.div(jop2, mode);
      Decimal cdiv = cop1.div(cop2, mode);
      jmeta = jdiv.meta();
      cmeta = cdiv.meta();
      jdiv.release();
      cdiv.release();
      if (!jmeta.equals(cmeta))
        abort("%s / %s (%s):\n%s\n%s", jmeta1.value, jmeta2.value, mode, jmeta, cmeta);
    }

    jop1.release();
    cop1.release();
    jop2.release();
    cop2.release();
  }


  public static String makeInteger (int minLength, int maxLength) {
    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    if (random.nextDouble() > 0.5)
      sb.append('-');
    int length = minLength + random.nextInt(maxLength - minLength);
    for (int i = 0; i < length; ++i)
      sb.append((char)(random.nextInt(10) + '0'));
    return sb.toString();
  }

  public static String makeDecimal (int minIntegerLength, int maxIntegerLength, int minDecimalLength, int maxDecimalLength) {
    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    if (random.nextDouble() > 0.5)
      sb.append('-');
    int intLength = minIntegerLength + random.nextInt(maxIntegerLength - minIntegerLength);
    for (int i = 0; i < intLength; ++i)
      sb.append((char)(random.nextInt(10) + '0'));
    sb.append('.');
    int decLength = minDecimalLength + random.nextInt(maxDecimalLength - minDecimalLength);
    for (int i = 0; i < decLength; ++i)
      sb.append((char)(random.nextInt(10) + '0'));
    return sb.toString();
  }


  static {
    System.loadLibrary("jlib");
  }
}
